/**
 * Created by IntelliJ IDEA.
 * User: martlenn
 * Date: 24-Jan-2008
 * Time: 04:59:24
 */
package org.hkupp.io;

import org.hkupp.db.accessors.Peptide;
import org.hkupp.db.accessors.Protein;
import org.hkupp.model.IdentifiedRun;
import org.hkupp.util.SeparatedStringSplitter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/*
 * CVS information:
 *
 * $Revision$
 * $Date$
 */

/**
 * This class reads a SPectrum Mill tag summary file (tsv formatted)
 * and transforms it into an IdentifiedRun object.
 *
 * @author Lennart Martens
 * @version $Id$
 */
public class TagSummaryReader {

    public static IdentifiedRun readTagSummaryFile(File aTagSummaryFile) throws IOException {
        return readTagSummaryFile(aTagSummaryFile, new HashMap<String, Protein>());
    }

    public static IdentifiedRun readTagSummaryFile(File aTagSummaryFile, HashMap<String, Protein> aProteins) throws IOException {

        // Variables.
        HashMap<String, Integer> locations = new HashMap<String, Integer>();

        HashMap<String, Peptide> peptides = new HashMap<String, Peptide>();
        HashMap<String, Protein> proteins = aProteins;

        HashMap<String, ArrayList<String>> peptideToProteinMap = new HashMap<String, ArrayList<String>>();
        HashMap<String, ArrayList<String>> proteinToPeptideMap = new HashMap<String, ArrayList<String>>();

        HashMap<String, ArrayList<Integer>> peptideLocationInProtein = new HashMap<String, ArrayList<Integer>>();

        // Parse the location and iteration from the filename.
        String runName = aTagSummaryFile.getName();
        String locationCode = null;
        int iteration = -1;
        boolean isWash = false;
        int washLoc = runName.indexOf("wash");
        if(washLoc >= 0) {
            isWash = true;
            runName = runName.substring(0, washLoc) + runName.substring(washLoc+4);
        }
        if(runName.startsWith("NF")) {
            // Non-fractionated data.
            int dashPos = runName.indexOf("-");
            locationCode = "O" + runName.substring(2, dashPos).trim();
            if(isWash) {
                locationCode += "-wash";
            }
            iteration = Integer.parseInt(runName.substring(dashPos+1).trim());
        } else if(runName.startsWith("F")) {
            // Pre-fractionated data.
            locationCode = convertLocation(runName);
            if(isWash) {
                locationCode += "-wash";
            }
            iteration = Integer.parseInt(runName.substring(runName.lastIndexOf("-")+1).trim());
        } else if(runName.startsWith("BSA")) {
            // BSA data.
            locationCode = "BSA";
            if(isWash) {
                locationCode += "-wash";
            }
            iteration = 1;
        }


        // Start reading the file.
        BufferedReader br = new BufferedReader(new FileReader(aTagSummaryFile));
        String line = null;
        int lineCount = 0;
        while((line = br.readLine()) != null) {
            lineCount++;
            line = line.trim();
            if(line.startsWith("filename\tscore") || line.equals("")) {
                // Header or empty line; skip.
                continue;
            }
            // Not a header line, parse info.
            SeparatedStringSplitter ssp = new SeparatedStringSplitter(line, "\t", true);
            if(ssp.countTokens() != 54) {
                System.err.println("  *** Unable to parse line " + lineCount + "; found " + ssp.countTokens() + " instead of the expected 54!");
            }

            // Read the desired data.
            String specFilename = ssp.nextToken();
            Double score = parseDouble(ssp.nextToken());
            Double percentScoredInt = parseDouble(ssp.nextToken());
            Double backboneScore = parseDouble(ssp.nextToken());
            Integer numUnusedIons = parseInteger(ssp.nextToken());
            Integer numFragmentIons = parseInteger(ssp.nextToken());
            Double revScore = parseDouble(ssp.nextToken());
            Double revPercentScoredInt = parseDouble(ssp.nextToken());
            Double deltaRevForward = parseDouble(ssp.nextToken());
            Double rank2Score = parseDouble(ssp.nextToken());
            Double rank2PercentScoredInt = parseDouble(ssp.nextToken());
            Double deltaRank2Forward = parseDouble(ssp.nextToken());
            String modification = ssp.nextToken();
            String varMods = ssp.nextToken();
            String previousAA = ssp.nextToken();
            String sequence = ssp.nextToken();
            String nextAA = ssp.nextToken();
            String fragmentIonMap = ssp.nextToken();
            String revSequence = ssp.nextToken();
            String rank2Sequence = ssp.nextToken();
            Double parentMZ = parseDouble(ssp.nextToken());
            Integer parentCharge = parseInteger(ssp.nextToken());
            Double matchedParentMass = parseDouble(ssp.nextToken());
            Double deltaParentMass = parseDouble(ssp.nextToken());
            Double retentionTime = parseDouble(ssp.nextToken());
            Double proteinMW = parseDouble(ssp.nextToken());
            Double peptidePI = parseDouble(ssp.nextToken());
            Double proteinPI = parseDouble(ssp.nextToken());
            String species = ssp.nextToken();
            String accession = ssp.nextToken();
            Integer indexNbr = parseInteger(ssp.nextToken());
            String coverage = ssp.nextToken();
            String database = ssp.nextToken();
            String enzyme = ssp.nextToken();
            Integer missedCleavages = parseInteger(ssp.nextToken());
            String parentMassConvert = ssp.nextToken();
            String cys = ssp.nextToken();
            String nterm = ssp.nextToken();
            String cterm = ssp.nextToken();
            String fixedMods = ssp.nextToken();
            Integer searchCycle = parseInteger(ssp.nextToken());
            String fixedModsParam = ssp.nextToken();
            String varModsParam = ssp.nextToken();
            String toleranceUnit = ssp.nextToken();
            Double parentMassTolerance = parseDouble(ssp.nextToken());
            Double fragmentMassTolerance = parseDouble(ssp.nextToken());
            String instrument = ssp.nextToken();
            String searchType = ssp.nextToken();
            Integer numMatchingSequences = parseInteger(ssp.nextToken());
            Integer mutationMatrixOff = parseInteger(ssp.nextToken());
            Integer starIonsOff = parseInteger(ssp.nextToken());
            String parentShiftType = ssp.nextToken();
            Double parentShift = parseDouble(ssp.nextToken());
            String entryName = ssp.nextToken();

            // Process coverage String.
            int pepStart = getPeptideStart(coverage);
            int protLength = getProteinLength(coverage);

            // Process spectrum filename (remove '.spo')
            specFilename = specFilename.substring(0, specFilename.lastIndexOf(".spo"));

            String peptideKey = specFilename + "@" + sequence;

            Peptide peptide = null;
            if(peptides.containsKey(peptideKey)) {
                peptide = peptides.get(peptideKey);
            } else {
                // Create a new peptide.
                peptide = new Peptide();
                if(backboneScore != null) {
                    peptide.setBackbone_cleavage_score(backboneScore.longValue());
                }
                peptide.setCterm(cterm);
                peptide.setCys(cys);
                peptide.setDelta_parent_mass(deltaParentMass);
                peptide.setDeltaforwardreversescore(deltaRevForward);
                peptide.setDeltarank1rank2score(deltaRank2Forward);
                peptide.setFragment_mass_tolerance(fragmentMassTolerance);
                peptide.setFragmentionmap(fragmentIonMap);
                peptide.setMatched_parent_mass(matchedParentMass);
                peptide.setMutation_matrix_off(mutationMatrixOff);
                peptide.setNext_aa(nextAA);
                peptide.setNterm(nterm);
                if(numFragmentIons != null) {
                    peptide.setNum_fragment_ions(numFragmentIons.longValue());
                }
                if(numMatchingSequences != null) {
                    peptide.setNum_seqs_past_parent_filter(numMatchingSequences.longValue());
                }
                if(numUnusedIons != null) {
                    peptide.setNum_unused_ions(numUnusedIons.longValue());
                }
                if(parentCharge != null) {
                    peptide.setParent_charge(parentCharge.longValue());
                }
                peptide.setParent_m_over_z(parentMZ);
                peptide.setParent_mass_convert(parentMassConvert);
                peptide.setParent_mass_tolerance(parentMassTolerance);
                peptide.setParent_shift(parentShift);
                peptide.setParent_shift_type(parentShiftType);
                peptide.setPeptide_pi(peptidePI);
                peptide.setPercent_scored_peak_intensity(percentScoredInt);
                peptide.setPrevious_aa(previousAA);
                peptide.setRank2_percent_scored_peak_intensity(rank2PercentScoredInt);
                peptide.setRank2score(rank2Score);
                peptide.setRank2sequence(rank2Sequence);
                if(retentionTime != null) {
                    peptide.setRetentiontimeconstant(retentionTime);
                }
                peptide.setReverse_percent_scored_peak_intensity(revPercentScoredInt);
                peptide.setReversescore(revScore);
                peptide.setReversesequence(revSequence);
                peptide.setScore(score);
                peptide.setSearch_type(searchType);
                peptide.setSequence(sequence);
                peptide.setStar_ions_off(starIonsOff);
                peptide.setTolerance_units(toleranceUnit);

                // Process the spectrum.
                File specFile = new File(aTagSummaryFile.getParentFile(), "cpick_in/" + specFilename);
                if(!specFile.exists()) {
                    throw new IOException("Unable to load spectrumfile '" + specFile.getAbsolutePath() + "' !");
                }
                peptide.setSpectrumFile(specFile);

                // Process the mods.
                peptide.setVarMods(varMods);
                peptide.setFixedMods(fixedMods);

                // Add the peptides to the peptides map.
                Object temp = peptides.put(peptideKey, peptide);
                if(temp != null) {
                    System.err.println(" ### Created new Peptide instance, yet one was already known! Key: '" + peptideKey + "'.");
                }
            }

            // Now the protein.
            Protein protein = null;
            if(proteins.containsKey(accession)) {
                protein = proteins.get(accession);
            } else {
                // Create a new one!
                protein = new Protein();
                protein.setAccession_number(accession);
                protein.setDatabase_name(database);
                protein.setEntry_name(entryName);
                protein.setProtein_mw(proteinMW);
                protein.setProtein_pi(proteinPI);
                protein.setSpecies(species);
                protein.setProteinlength(protLength);

                // Add the protein to the proteins map.
                proteins.put(accession, protein);
            }

            // Now check the mappings.
            // First peptide.
            if(peptideToProteinMap.containsKey(peptideKey)) {
                ArrayList<String> proteinList = peptideToProteinMap.get(peptideKey);
                if(!proteinList.contains(accession)) {
                    proteinList.add(accession);
                }
            } else {
                ArrayList<String> proteinList = new ArrayList<String>();
                proteinList.add(accession);
                peptideToProteinMap.put(peptideKey, proteinList);
            }

            // Then protein.
            if(proteinToPeptideMap.containsKey(accession)) {
                ArrayList<String> peptideList = proteinToPeptideMap.get(accession);
                if(!peptideList.contains(peptideKey)) {
                    peptideList.add(peptideKey);
                }
            } else {
                ArrayList<String> peptideList = new ArrayList<String>();
                peptideList.add(peptideKey);
                proteinToPeptideMap.put(accession, peptideList);
            }

            // Finally, location.
            if(!peptideLocationInProtein.containsKey(sequence + "@" + accession)) {
                // New location.
                ArrayList<Integer> pepLocs = new ArrayList<Integer>();
                pepLocs.add(new Integer(pepStart));
                peptideLocationInProtein.put(sequence + "@" + accession, pepLocs);
            } else {
                // Sanity check.
                ArrayList pepLocs = peptideLocationInProtein.get(sequence + "@" + accession);
                if(!pepLocs.contains(new Integer(pepStart))) {
                    pepLocs.add(new Integer(pepStart));
                }

            }
        }
        br.close();


        IdentifiedRun result = new IdentifiedRun(iteration, locationCode, peptides, peptideToProteinMap, proteins, proteinToPeptideMap, peptideLocationInProtein);

        return result;
    }

    private static Double parseDouble(String aString) {
        Double result = null;
        if(aString != null && !aString.equals("")) {
            result = new Double(aString);
        }
        return result;
    }

    private static Integer parseInteger(String aString) {
        Integer result = null;
        if(aString != null && !aString.equals("")) {
            result = new Integer(aString);
        }
        return result;
    }

    private static int getPeptideStart(String aCoverage) {
        int location1 = aCoverage.indexOf("+")+1;
        int location2 = aCoverage.indexOf("+", location1);
        return Integer.parseInt(aCoverage.substring(location1, location2).trim());
    }

    private static int getProteinLength(String aCoverage) {
        int location = aCoverage.lastIndexOf("+")+1;
        return Integer.parseInt(aCoverage.substring(location).trim());
    }

    private static String convertLocation(String aRunName) {
        int dashPos1 = aRunName.indexOf("-");
        int dashPos2 = aRunName.indexOf("-", dashPos1+1);
        int ief = Integer.parseInt(aRunName.substring(1,dashPos1).trim());
        String iefLetter = null;
        switch(ief) {
            case 1:
                iefLetter = "A";
                break;
            case 2:
                iefLetter = "B";
                break;
            case 3:
                iefLetter = "C";
                break;
            case 4:
                iefLetter = "D";
                break;
            case 5:
                iefLetter = "E";
                break;
            case 6:
                iefLetter = "F";
                break;
            case 7:
                iefLetter = "G";
                break;
            case 8:
                iefLetter = "H";
                break;
            case 9:
                iefLetter = "I";
                break;
            case 10:
                iefLetter = "J";
                break;
            default:
                throw new RuntimeException("This software supports only up to 10 prefractionation fractions!");
        }
        String lane = aRunName.substring(dashPos1+1, dashPos2).trim();
        return iefLetter + lane;
    }

    public static void main(String[] args) {
        // For testing purposes.
        try {
            TagSummaryReader.readTagSummaryFile(new File("D:\\lennart\\ebi\\projects\\HUPO\\HUPO_KUPP\\spectrummill_output\\061003\\F1-01-1\\tagSummary.1.tsv"));
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }
    }
}
